package com.itheima.reggie.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.reggie.common.R;
import com.itheima.reggie.entity.Employee;
import com.itheima.reggie.service.EmployeeService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;

/**
 * 登录页面
 *      前端发送的请求/employee/login
 *      设置路径
 *
 *      多重判断
 *          ·数据库存储的是加密后的密码
 *              先将密码进行加密
 *          ·数据库用户名设置了唯一
 *              通过用户名进行查询
 *          密码判断是否符合
 *          ·数据库设置了状态
 *              状态判断是否可以通过
 *          ·用户确认成功后，为了后面继续使用，存储在session里面
 *              session存储id，返回登录成功
 *
 *      在进行调试的时候，等待时间过长，设置request.js等待时间
 *
 *  退出页面
 *      当用户点击退出时，要删除所有相关信息
 *          后台存储的session，页面存储的信息
 *
 *          ·后端
 *              使用request移除存储的session，返回成功结果
 *          ·前端
 *              移除application存储的信息
 *
 *   新增员工
 *      用户点击保存按钮时，将页面的用户信息发送过来
 *          获取页面数据
 *          对用户数据进行补充
 *              密码使用加密处理
 *              创建者获取session值进行查询id存储
 *
 *      账号名是唯一标识，当账号名重复时，会报SQLIntegrityConstraintViolationException异常
 *          使用全局异常处理器进行处理
 *
 *
 *   分页条件查询
 *      分页展示页面信息
 *          前端
 *              前端在页面设置page，pageSize，name作为页面展示条件
 *              默认name为空
 *              发送请求时，为get类型请求，将三个参数封装为一个对象传递
 *                  设置拦截器拦截get请求，拆分对象，拼接在请求地址
 *          后端
 *              后端进行分页查询，首先开启MP的分页查询拦截器
 *                  创建分页类，创建MP拦截器方法，添加page拦截器
 *              接收请求，创建方法
 *                  请求为普通get请求，直接接收参数
 *                  与前端对应的返回值，使用Page类型参数返回
 *                      创建分页构造器，传入page，pageSize
 *                      创建条件构造器，添加条件
 *                      调用查询方法，传入page对象和条件
 *                      返回page对象
 *
 *  修改员工状态
 *      ·前端
 *          页面传递数据到前端展示，js接收数据时，会将Long类型失精
 *          导致修改时传递id不同，后台无法修改
 *      ·后端
 *          创建JacksonObjectMapper对象转换器进行json对象和java对象的转换，防止失精
 *              web容器有自己的转换器集合
 *              创建消息转换器，将自己的json对象转换器添加到消息转换器，替换web容器的首位(index=0)转换器
 *
 *       ·前端
 *          页面点击修改状态，发送id，status到后端
 *              status和“启用”“禁用”反向展示，发送status时，反向发送status进行修改
 *       ·后端
 *          接收传递id，status，封装成对象
 *          设置对象修改时间，修改人，状态
 *          调用修改方法，返回信息
 *
 *  修改员工信息
 *      ·数据回显
 *          前端
 *              点击修改按钮，携带当前行的id，跳转到add页面
 *              add页面根据是否携带参数进行判断，赋值为edit
 *              调用init方法，发送异步请求，携带id，查询用户信息
 *              根据返回值，sex赋值，传递到表单
 *          后端
 *              接收传递的用户id，根据id查询数据
 *              判断对象是否为空，进行返回
 *      ·修改信息
 *          前端
 *              修改用户信息，点击保存
 *              发送异步请求，sex赋值，发送异步请求，携带表单数据
 *              根据返回值进行判断，进行页面跳转
 *          后端
 *              使用修改用户状态方法
 *
 *   公共字段自动填充
 *      新增和修改员工数据时
 *          修改时间，创建时间，修改人都是相同代码，简化代码书写
 *      ·后端
 *          对实体类的字段上添加@TableFiled(fill = FieldFill.INSERT or INSERT_UPDATE)
 *          创建MyMetaObjectHandler类实现接口
 *          使用@Component注解，交给spring管理
 *              重写方法
 *                  设置需要控制的字段值
 *                  设置自动填充的值
 *
 *      获取修改者id
 *          发送请求经历的过程
 *              发送请求
 *              LoginCheckFilter进行拦截
 *              controller进行接收
 *              service层
 *              MyMetaObjectHandler进行填充
 *          请求过程共用一个线程
 *          ThreadLocal类时Thread的一个属性，可以存储数据
 *              创建BaseContext类，在ThreadLocal类的基础上
 *              对修改者信息进行设置
 *                  创建ThreadLocal对象，对get和set方法进行封装，作为静态工具类使用
 *                      在LoginCheckFilter阶段调用set方法将修改者id存入
 *                      在MyMetaObjectHandler阶段调用get方法取出id
 *
 */

@Slf4j
@RestController
@RequestMapping("/employee")
public class EmployeeController {

    @Autowired
    private EmployeeService service;

    /**
     * 登录请求
     * @param request
     * @param employee
     * @return
     */
    @PostMapping("/login")
    public R<Employee> login(HttpServletRequest request, @RequestBody Employee employee){

        //①. 将页面提交的密码password进行md5加密处理, 得到加密后的字符串
        String password = employee.getPassword();
        password = DigestUtils.md5DigestAsHex(password.getBytes());

        //②. 根据页面提交的用户名username查询数据库中员工数据信息
        LambdaQueryWrapper<Employee> lqw = new LambdaQueryWrapper<>();
        lqw.eq(Employee::getUsername,employee.getUsername());
        Employee emp = service.getOne(lqw);

        //③. 如果没有查询到, 则返回登录失败结果
        if (emp == null){
            return R.error("登录失败");
        }

        //④. 密码比对，如果不一致, 则返回登录失败结果
        if (!password.equals(emp.getPassword())){
            return R.error("登录失败");
        }

        //⑤. 查看员工状态，如果为已禁用状态，则返回员工已禁用结果
        if (emp.getStatus() == 0){
            return R.error("用户被禁用");
        }

        //⑥. 登录成功，将员工id存入Session, 并返回登录成功结果
        request.getSession().setAttribute("employee",emp.getId());
        return R.success(emp);
    }

    /**
     * 退出请求
     * @param request
     * @return
     */
    @PostMapping("/logout")
    public R<String> logout(HttpServletRequest request){
        //退出登录，将session里面存储的用户信息移除，返回成功信息
        request.getSession().removeAttribute("employee");
        return R.success("退出成功");
    }

    /**
     * 新增员工请求
     * @param request
     * @param employee
     * @return
     */
    @PostMapping
    public R<String> save(HttpServletRequest request,@RequestBody Employee employee){

        //1.获取页面传递的用户信息
        log.info("用户信息：{}",employee);

        //2.将信息补充完整
        employee.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));
        employee.setCreateTime(LocalDateTime.now());
        employee.setUpdateTime(LocalDateTime.now());
        Long id = (Long)request.getSession().getAttribute("employee");
        employee.setCreateUser(id);
        employee.setUpdateUser(id);

        //3.调用添加方法，将信息添加，返回成功信息
        service.save(employee);
        return R.success("添加成功");
    }

    /**
     * 分页查询
     * @param page
     * @param pageSize
     * @param name
     * @return
     */
    @GetMapping("/page")
    public R<Page> selectByPage(int page,int pageSize,String name){
        //http://localhost/employee/page?page=1&pageSize=10&name=aaa
        log.info("page = {},pageSize = {},name = {}" ,page,pageSize,name);

        //创建分页构造器
        Page pageInfo = new Page(page,pageSize);

        //创建条件构造器
        LambdaQueryWrapper<Employee> lqw = new LambdaQueryWrapper<>();

        //添加条件
        lqw.like(!StringUtils.isEmpty(name),Employee::getName,name);
        lqw.orderByDesc(Employee::getUpdateTime);

        //调用分页查询方法
        service.page(pageInfo,lqw);

        //返回查询结果
        return R.success(pageInfo);
    }

    /**
     * 员工信息修改
     * @param employee
     * @return
     */
    @PutMapping
    public R<String> update(HttpServletRequest request,@RequestBody Employee employee){
        log.info(employee.toString());

        //修改员工修改时间，修改人,状态
        employee.setUpdateTime(LocalDateTime.now());
        Long empId = (Long) request.getSession().getAttribute("employee");
        employee.setUpdateUser(empId);
        employee.setStatus(employee.getStatus());

        //调用修改方法
        service.updateById(employee);

        //返回信息
        return R.success("修改成功");
    }

    /**
     * 根据id查询
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    public R<Employee> getById(@PathVariable Long id){

        //获取传过来的id
        Employee emp = service.getById(id);

        //严谨性判断，返回用户信息
        if(emp != null){
            return R.success(emp);
        }

        //返回错误
        return R.error("查无用户");
    }
}
